# adjust cwd and sys.path
def adjust_to_correct_appdir():
    import os, sys
    try:
        appdir = __file__
        print appdir
        if not appdir:
            raise ValueError
        appdir = os.path.abspath(os.path.dirname(__file__))
        os.chdir(appdir)
        sys.path = [ e for e in sys.path if e != appdir] 
        sys.path.insert(0,os.path.join(appdir, 'libs'))
        sys.path.insert(0,os.path.join(appdir, 'game_src'))
    except ValueError:
        print 'Please run from an OS console.'
        import time
        time.sleep(10)
        sys.exit(1)
adjust_to_correct_appdir()

### handle options
##def get_options():
##    import optparse
##    parser = optparse.OptionParser()
##    parser.add_option("-x", "--width", type='int', dest="width", default='800',
##                      help="set window width", metavar="WIDTH")
##    parser.add_option("-y", "--height", type="int", dest="height", default='600',
##                      help="set window height", metavar="HEIGHT")
##    parser.add_option("-F", "--fullscreen", dest="fullscreen", default=False,
##                      help="set fullscreen mode on", action="store_true")
##    parser.add_option("--startlevel", type='int', dest="startlevel", default='-1',
##                      help="first level to play", metavar="STARTLEVEL")
##    parser.add_option("--sound", dest="sound", default=True,
##                      help="enables sound, default True", metavar="sound")
##    (options, args) = parser.parse_args()
##    members = ['width', 'height', 'fullscreen', 'startlevel', 'sound']
##    d = {}
##    for k in members:
##        d[k] = getattr(options, k)
##    return d
try:
    from xml.etree import ElementTree
except ImportError:
    import elementtree.ElementTree as ElementTree
##getchildren: Returns all subelements. The elements are returned in
##document order.

import pickle
import os

import map_consts as mc

def list_children(node):
    for e in node.getchildren():
        print '\n', e.tag
        print 'keys:'
        print e.keys()
        print 'id:', e.get('id', None)

#########################################################################
# copied from cocos\tiles to read properties, dont want to import cocos
_xml_to_python = dict(
    unicode=unicode,
    int=int,
    float=float,
    bool=lambda value: value != "False",
)

class TilesPropertyWithoutName(Exception):
    pass

class TilesPropertyWithoutValue(Exception):
    pass

def _handle_properties(tag):
    """returns the properties dict reading from the etree node tag

    :Parameters:
        `tag` : xml.etree.ElementTree
            node from which the properties are obtained
    """
    properties = {}
    for node in tag.findall('./property'):
        name = node.get('name')
        type = node.get('type') or 'unicode'
        value = node.get('value')
        if name is None:
            raise TilesPropertyWithoutName("\nnode:\n%s"%ElementTree.tostring(node))
        if value is None:
            raise TilesPropertyWithoutValue("\nnode:\n%s"%ElementTree.tostring(node))
        properties[name] = _xml_to_python[type](value)
    return properties
#########################################################################






# tile_matrix
# a dict such that point (x,y) lies in the cell described by
# d[x//tlenght, y//tlenght], value is the tile that covers the cell
#
# tal como esta los huecos no son ingresados, si se desea habria que
# agregar un else al if tile is not none

# haciendo un mapa vacio salvo la celda bottomleft, resulto que la celda
# [(0,0)] fue la que recibio el valor, asi que las coords openGL son
# compatibles con el orden de la matriz

# verificamos con una matriz rectangular y se obtienen el nro de filas y
# columnas esperados
def load_layer_as_matrix(node, get_properties=False):
    assert node.tag == 'rectmap'
    d = {}
    if get_properties:
        properties = {}
    else:
        properties = None
    xx = 0
    len_yy = 0
    for col in node.getchildren():
        if col.tag != 'column':
            print '*** child of rectmap is not column - tag:', col.tag
            continue
        yy = 0
        for cell in col.getchildren():
            if cell.tag != 'cell':
                print '*** child of column is not cell'
                continue
            tile = cell.get('tile', None)
            if  tile is not None:
                d[(xx, yy)] = tile
                if get_properties:
                    properties[(xx, yy)] = _handle_properties(cell)
            yy += 1
        if len_yy>0:
            assert len_yy == yy
        else:
            len_yy = yy
        xx += 1
    len_xx = xx
    #print len_xx, len_yy
    d['len_xx'] = len_xx
    d['len_yy'] = len_yy
    return d, properties


    
##    # mat[yy][xx] is column xx , row yy , stored by row, 0 based
##    mat = [ [0]*tile_matrix['len_xx'] for k in xrange(tile_matrix['len_yy'])]
##
##    # index order is the tile_matrix inverse order, so for consistency
##    # I choose
##
##    # mat[xx][yy] is column xx , row yy , stored by column, 0 based
##    mat = [ [0]*tile_matrix['len_yy'] for k in xrange(tile_matrix['len_xx'])]
##
##    A point (x,y) will lie in the cell mapped to
##        mat[x//tlenght][y//tlenght]
##
##    And it is true that
##    len_xx == len(mat) == number of columns
##    len_yy == len(mat[0]) == number of rows

def add_touch(tile_matrix, mat):
    if mat is None:
        mat = [ [mc.EMPTY]*tile_matrix['len_yy'] for k in xrange(tile_matrix['len_xx'])]

    for xy in tile_matrix:
        try:
            xx, yy = xy
        except ValueError:
            # skip the len_xx and len_yy keys
            continue
        tile = tile_matrix[xy]
        mat[xx][yy] |= mc.tile_to_flags[tile]
    return mat

def del_invisible_layers(root, retain_id):
#    print '\nin del'
    for e in list(root):
#        print '\tnode', e.tag, e.get('id', None),
        if e.tag == 'rectmap' and e.get('id') != retain_id:
#            print 'removed'
            root.remove(e)
        else:
            print

# patrol is a sequence of points, no duplicates, ordered by 'sec' value
# cannot specify velocity per trait.
# stored one per layer, layer must be called patrol*
def parse_patrol(node):
    # to this time only 'vmul' layer property is required
    layer_props = _handle_properties(node)
    node_name = node.get('id')
    tile_matrix, properties = load_layer_as_matrix(node, get_properties=True)
    tile_matrix.pop('len_xx')
    tile_matrix.pop('len_yy')
    li = []
    for xy in tile_matrix:
        try:
            sec = properties[xy]['sec']
        except KeyError:
            print node_name, xy, "missing 'sec' property"
            continue
        li.append((sec, xy))
    li.sort(key=lambda e: e[0])

    # sanity: no duplicate sec numbers
    try:
        assert len(li)==len(set([a for a,b in li]))
    except AssertionError:
        print node_name , "has duplicated 'sec' property:",li 

    return [xy for a,xy in li], layer_props

def get_patrol_offsets(collected):
    """side effect: points """
    # get patrols by id
    patrols = collected['patrols']

    # get terminals by id
    terminals =  {}
    for tile, xy, prop in collected['actors']:
        if tile == 'terminal':
            if prop is None or 'id' not in prop:
                print 'ERR: Terminal missing id - xy:', xy
                continue
            key = prop['id']
            if key in terminals:
                print "ERR: Duplicated Terminal id=%s - xy=%s"%key, xy
                print "\tother id, xy:", key, terminals[key][0]
                continue
            if 'patrol' not in prop:
                print 'WARN: terminal missing patrol - id, xy:', key, xy
                initial_patrol = None
            else:
                initial_patrol = prop['patrol']
                if initial_patrol not in patrols:
                    print 'ERR: Terminal refers to unknown terminal - id:', key
                    continue
            terminals[key] = (xy, initial_patrol)

    # initialize to (0,0) each element in the offset table
    offsets = {}
    for k1 in terminals:
        offsets[k1] = {}
        for k2 in patrols:
            offsets[k1][k2] = (0, 0)

    # calc the offsets defined  (others must be tweaked in game)
    for k1 in terminals:
        xy, initial_patrol = terminals[k1]
        offsets[k1][initial_patrol] = xy

        # make the points relative to xy
        points, props = patrols[initial_patrol]
        x0, y0 = xy
        points = [ (x-x0, y-y0) for x,y in points]
        patrols[initial_patrol] = (points, props)

    # for patrols initially not asociated with a terminal make the path
    # relative to first point
    #? TODO

    return offsets

    
def compile_level(basename):
    fname = 'data/' + basename + '.xml'
    tree = ElementTree.parse(fname)
    root = tree.getroot()
    assert root.tag=='resource'
    collected = {
        'tile_size': None, #(width, height)
        'patrols': {}, # entries are patrolname: list of points (order matters) 
        'actors': None, # list of (tilename, pos, prop_dict), prop_dict None if no props
        'touch': None # touch matrix
        }
    touch_matrix = None
    terminal_names = []
    terminals = []
    for e in root.getchildren():
        if e.tag == 'resource':
            pass
        if e.tag == 'rectmap':
            #each rectmap is a layer
            idx = e.get('id')
            if idx == 'actors':
                node_actors = e
                tile_matrix, prop = load_layer_as_matrix(e, get_properties=True)
                touch_matrix = add_touch(tile_matrix, touch_matrix)

                tile_matrix.pop('len_xx')
                tile_matrix.pop('len_yy')
                collected['actors'] = [ (tile_matrix[xy], xy, prop.get(xy, None))
                                                        for xy in tile_matrix]

            elif idx == basename:
                node_terrain = e
                # asume all layers with same tile_size
                s = e.get('tile_size')
                li = s.split('x')
                collected['tile_size'] = (int(li[0]), int(li[1]))
                tile_matrix, _  = load_layer_as_matrix(e, get_properties=False) 
                touch_matrix = add_touch(tile_matrix, touch_matrix)
                
            elif idx.startswith('patrol'):
                collected['patrols'][idx] = parse_patrol(e)

    collected['touch'] = touch_matrix

    # to swap the patrol between terminals each patrol must have an offset
    # from the terminal
    # In game, a floater doing the patrol k2 in terminal k1 will move over the
    # poly [offsets[k1][k2] + xy for xy in points]
    patrol_offsets = get_patrol_offsets(collected)
    fname = 'data/' + basename + '.tweaks.pkl'
    if os.path.exists(fname):
        print '\n\nWARN: mixed old and new - tweak file exists:',fname
        print
        f=open(fname, 'rb')
        old = pickle.load(f)
        f.close()
        # old info overwrites the new one where overlaps
        # will fail if deleted a terminal or patrol in the new version
        for k1 in old:
            d = old[k1]
            for k2 in d:
                try:
                    patrol_offsets[k1][k2] = d[k2]
                except KeyError:
                    pass
    f=open(fname, 'wb')
    pickle.dump(patrol_offsets,f)
    f.close()
        
#? si detecta tweaks, tal como esta el codigo se corrompen los points.
# en ese caso se podria calcular la nueva matriz de ofsets y luego
# superponerle la que esta en el tweaks del disco

    
    # save collected data
    f = open('data/' + basename+'.pkl', 'wb')
    pickle.dump(collected, f)
    f.close()

    # save a stripped version - warn: root is modified
    del_invisible_layers(root, basename)
    new_basename = basename + '.c'
    new_fname = 'data/' + new_basename + '.xml'
    node_terrain.set('id', new_basename)
    tree.write(new_fname)
    
    return collected
                
#basename = 'pat1'
basename = '0map'
info = compile_level(basename)
print info

#basename = 'bottomleft'
#basename = 'rect5-10'
##fname = 'data/' + basename + '.xml'
##tree = ElementTree.parse(fname)
##root = tree.getroot()
##assert root.tag=='resource'
##
####root : resource
####    requires
####    rectmap 0map
####    rectmap actors
##    
##
### separate in layers
##for e in root.getchildren():
##    print '\n', e.tag
##    print 'keys:'
##    print e.keys()
##    print 'id:', e.get('id')
##
##    idx = e.get('id')
##    if idx == 'actors':
##        node_actors = e
##    elif idx == basename:
##        node_terrain = e
##
##### test load_layer as matrix
####print '\n\nlayer terrain', 
####d, properties = load_layer_as_matrix(node_terrain, get_properties=False)
####print d
##
### test del invisible layers
##del_invisible_layers(root, basename)
##new_basename = 'zx'
##new_fname = 'data/' + new_basename + '.xml'
##node_terrain.set('id', new_basename)
##tree.write(new_fname)



# quizas otra info de root necesaria seria el cell size

